import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.text.DecimalFormat;

public class CodeScreenPanel extends JPanel implements Runnable
{
  private static final int NO_DELAYS_PER_YIELD = 16;
  /* Number of frames with a delay of 0 ms before the animation thread yields
     to other running threads. */

  private static int MAX_FRAME_SKIPS = 5;   // was 2;
    // no. of frames that can be skipped in any one animation loop
    // i.e the state is updated but not rendered

  private int pWidth, pHeight;   // dimensions of the panel
  private long startTime;
  private int x = 0;
  private int y =0;

  private Thread animator;           // the thread that performs the animation
  private volatile boolean running = false;   // used to stop the animation thread
  private volatile boolean isPaused = false;

  private long period;                // period between drawing in _nanosecs_

  // used at termination
  private volatile boolean gameOver = false;

  // off screen rendering
  private Graphics dbg;
  private Image dbImage = null;

  //font settings
  private int fontSize = 10;
  private int spacing = 4;
  private int maxChars;
  private int numLines;

  //page and viewport
  JFileChooser fc = new JFileChooser();

  Page p;
  //Page p = new Page("C:\\Users\\theevilelephant\\Desktop\\SomeCode.java", 80);
  ViewPort v;



  public CodeScreenPanel(long period, int w, int h)
  {
    //wcTop = wc;
    this.period = period;
    pWidth = w; pHeight = h;

    fc.showOpenDialog(this);

    if(fc.getSelectedFile()==null){
        System.exit(1);
    }
    p = new Page(fc.getSelectedFile().getAbsolutePath(), 80);
    v = new ViewPort(p, pHeight, pWidth, fontSize, spacing);
    setBackground(Color.white);
    setPreferredSize( new Dimension(pWidth, pHeight));

    setFocusable(true);
    requestFocus();    // the JPanel now has focus, so receives key events
	readyForTermination();


    gameOver = false;

   /* addMouseListener( new MouseAdapter() {
      public void mousePressed(MouseEvent e)
      { testPress(e.getX(), e.getY()); }
    });*/
  } 



  private void readyForTermination()
  {
	addKeyListener( new KeyAdapter() {
	// listen for esc, q, end, ctrl-c on the canvas to
	// allow a convenient exit from the full screen configuration
       public void keyPressed(KeyEvent e)
       { int keyCode = e.getKeyCode();
         if ((keyCode == KeyEvent.VK_ESCAPE) || (keyCode == KeyEvent.VK_Q) ||
             (keyCode == KeyEvent.VK_END) ||
             ((keyCode == KeyEvent.VK_C) && e.isControlDown()) ) {
           running = false;
         }
         else if(keyCode == KeyEvent.VK_SPACE){
             isPaused = !isPaused;
         }
       }
     });
  }  // end of readyForTermination()


  public void addNotify()
  // only start the animation once the JPanel has been added to the JFrame
  { super.addNotify();   // creates the peer
    start();    // start the thread
  }

  private void start()
  // initialise and start the thread
  { if (animator == null || !running) {
      animator = new Thread(this);
	  animator.start();
    }
  }


  // called by the JFrame's window listener methods

  public void resume()
  // called when the JFrame is activated / deiconified
  {  isPaused = false;  }


  public void pause()
  // called when the JFrame is deactivated / iconified
  { isPaused = true;   }


  public void stop()
  // called when the JFrame is closing
  {  running = false;   }

  // ----------------------------------------------


  private void testPress(int x, int y)
  // is (x,y) near the head or should an obstacle be added?
  {
     // this.x = x;
     // this.y = y;
  }  // end of testPress()


  public void run()
  /* The frames of the animation are drawn inside the while loop. */
  {
    long beforeTime, afterTime, timeDiff, sleepTime;
    long overSleepTime = 0L;
    int noDelays = 0;
    long excess = 0L;
    Graphics g;

    startTime = System.nanoTime();
    beforeTime = startTime;

	running = true;

	while(running) {
            Update();
            Render();   // render to a buffer
            paintScreen();  // draw the buffer on-screen

      afterTime = System.nanoTime();
      timeDiff = afterTime - beforeTime;
      sleepTime = (period - timeDiff) - overSleepTime;

      if (sleepTime > 0) {   // some time left in this cycle
        try {
          Thread.sleep(sleepTime/1000000L);  // nano -> ms
        }
        catch(InterruptedException ex){}
        overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
      }
      else {    // sleepTime <= 0; the frame took longer than the period
        excess -= sleepTime;  // store excess time value
        overSleepTime = 0L;

        if (++noDelays >= NO_DELAYS_PER_YIELD) {
          Thread.yield();   // give another thread a chance to run
          noDelays = 0;
        }
      }

      beforeTime = System.nanoTime();

      /* If frame animation is taking too long, update the state
         without rendering it, to get the updates/sec nearer to
         the required FPS. */
      int skips = 0;
      while((excess > period) && (skips < MAX_FRAME_SKIPS)) {
        excess -= period;
	    Update();    // update state but don't render
        skips++;
      }
//      framesSkipped += skips;

      //storeStats();
	}


    System.exit(0);   // so window disappears
  } // end of run()



  private void Update()
  { if (!isPaused && !gameOver){
         v.updateViewPort();  
    }

  } 


  private void Render()
  {
    if (dbImage == null){
      dbImage = createImage(pWidth, pHeight);
      if (dbImage == null) {
        System.out.println("dbImage is null");
        return;
      }
      else
        dbg = dbImage.getGraphics();
    }

    v.Render(dbg);


    //dbg.drawRect(x, y, 10, 10);
    // draw game elements: the obstacles and the worm


    if (gameOver)
      gameOverMessage(dbg);
  }  


  private void gameOverMessage(Graphics g)
  {
    g.drawString("File finished.....", 10, 10);
  }


  private void paintScreen()
  // use active rendering to put the buffered image on-screen
  {
    Graphics g;
    try {
      g = this.getGraphics();
      if ((g != null) && (dbImage != null))
        g.drawImage(dbImage, 0, 0, null);
      // Sync the display on some systems.
      // (on Linux, this fixes event queue problems)
      Toolkit.getDefaultToolkit().sync();
      g.dispose();
    }
    catch (Exception e)   // quite commonly seen at applet destruction
    { System.out.println("Graphics error: " + e);  }
  } // end of paintScreen()
}  // end of WormPanel class

